/*
    Video Multiplexer with Motion Detection response
	Jess Lewis, 1/20/11
	
	This code blinks a power on status indicator based upon the state of the mux.
	This code also triggers a VCR, through an IR LED interface, to record or pause.
	This code also triggers recording based upon the state of a motion sensor.
	This code also records the video output of two cameras, multiplexed by the time
	  setpoint indicated by potentiometer inputs at regular intervals regardless of 
	  motion detection.
	   
 * Used PIC16F690 device. */

#include <stdio.h>
#include <stdlib.h>
#include <htc.h> 
#include "analogs.h"
#include "timing.h"


#ifndef _XTAL_FREQ
 // Unless already defined assume 4MHz system frequency
 // This definition is required to calibrate __delay_us() and __delay_ms()
 #define _XTAL_FREQ 4000000
#endif

#define CAMERA1_ON_STPT   4
#define CAMERA2_ON_STPT   5
#define CAMERA_DLY_STPT  10


/*	NOTE:  max timeneeded or loop intervals is 16_777_215 as defined by the variable size (unsigned short long)
			with a PAUSE_INTERVAL of only 1ms and camera_stpt of 1275 the maximum is 1_275_000
			this means that the multiplier for camera_stpt could be as high as 13 
			thus yielding 3315 seconds or 55.25 minutes between recording intervals
			This would also cause the delay between recordings to be at least 13 seconds ie. 13 to 3315 
*/

//5 provides 5 - 1275 seconds yielding 21.75 minutes max between recordings
//56 provides 56 - 14280 seconds yielding 238 minutes max (3.967 hours) between recordings
#define DELAY_MULTIPLIER  56 
#define PAUSE_INTERVAL	 10 //duration in milliseconds for each pause within the pause loop

//time that each status indicator sequences takes in milliseconds.  It is important to code adjust the times
//to match this value or if the values must change in the Status_State() function then STATUS_TIME needs to be
//manually adjusted accordingly
#define STATUS_TIME		300 

#define CAM_SLCT		RC3
#define IR_LED		    RB7  
#define STATUS_LED		RA5
#define CAM_SLCT_RLY	RC7
#define MOTION_SENSOR	RB6


#define STNUMS			9 		//number of items in start_seq array
#define SEQNUMS			22		//number of items in ***_seq array

//values need to be multiplied by ten to provide times in microseconds
const unsigned char start_seq[STNUMS] = {46,146,46,146,46,51,51,46,51}; 
											
const unsigned char stop_btn_seq1[SEQNUMS] = {46,54,146,54,146,54,146,54,46,54,46,54,146,54,46,54,46,54,146,54,46,54}; 
const unsigned char stop_btn_seq2[SEQNUMS] = {60,54,46,54,46,54,46,54,146,54,146,54,46,54,146,54,146,54,46,54,146,54}; 

const unsigned char rec_btn_seq1[SEQNUMS] = {54,54,46,51,46,51,46,54,146,51,54,46,146,46,51,51,51,46,146,51,51,46}; 
const unsigned char rec_btn_seq2[SEQNUMS] = {68,46,151,46,151,46,151,51,51,46,151,46,51,51,151,46,151,46,46,51,151,46}; 


__CONFIG(INTIO & WDTDIS & PWRTDIS & MCLRDIS & UNPROTECT & BORDIS & IESODIS & FCMDIS);	


//function declarations
void config_io(void);
void StatusState(unsigned char);
void selectCamera(unsigned char); 
void output38khz(void);
char need38khz(char z);
void setTimer2(void);
void send40msgap(void);
void sendstartseq(void);
void stopBtn(void);
void recBtn(void);
void StatusState(unsigned char setstate);
void selectCamera(unsigned char camneeded);


void config_io(void){
	
 	TRISA0 = 0; // UNUSED
	TRISA1 = 0; // UNUSED
	TRISA2 = 0; // SPARE3
	TRISA3 = 0; // UNUSED
	TRISA4 = 0; // UNUSED
	TRISA5 = 0; // STATUS LED
	
	TRISB4 = 1; // ANALOG RUNTIME 
	TRISB5 = 0; // UNUSED
	TRISB6 = 1; // PIR INPUT
	TRISB7 = 0; // SPARE
	
	TRISC0 = 1; // ANALOG DELAY TIME 
	TRISC1 = 1; // ANALOG ON TIME
	TRISC2 = 0; // UNUSED 
	TRISC3 = 1; // CAMERA SELECT SWITCH 
	TRISC4 = 0; // VCR_REC_SELECT
	TRISC5 = 0; // VCR_PAUSE_SELECT
	TRISC6 = 0; // SPARE1
	TRISC7 = 0; // CAMERA_CTRL

	ANS0 = 0; // UNUSED 
	ANS1 = 0; // UNUSED 
	ANS2 = 0; // UNUSED 
	ANS3 = 0; // UNUSED 
	ANS4 = 1; // DELAY TIME
	ANS5 = 1; // ON TIME
	ANS6 = 0; // UNUSED
	ANS7 = 0; // UNUSED
	ANS8 = 0; // UNUSED
	ANS9 = 0; // UNUSED
	ANS10 = 1; // RUN TIME
	ANS11 = 0; // UNUSED	

	C1ON = 0;
	C1OE = 0;
	C2ON = 0;
	C2OE = 0;
}


void output38khz(void){
	//note that the following while loop was tested on a continuous loop and
	//observed with an o-scope to yield 40khz exactly.
	timing = 0;

	while(timing == 0)
	{
		//this has to oscillate the IR LED at 38khz until this while is over
		IR_LED = 0;
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();	
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
    	IR_LED = 1;
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
		NOP();
	}
}


char need38khz(char z){
	char temp = 0;

	switch(z)
	{
		case 0:
			temp = 1;
			break;
		case 2:
			temp = 1;
			break;
		case 4:
			temp = 1;
			break;
		case 6:
			temp = 1;
			break;
		case 8:
			temp = 1;
			break;
		case 10:
			temp = 1;
			break;
		case 12:
			temp = 1;
			break;
		case 14:
			temp = 1;
			break;
		case 16:
			temp = 1;
			break;
		case 18:
			temp = 1;
			break;
		case 20:
			temp = 1;
			break;
		default:
			temp = 0;
			break;
	}		

	return temp;
}

void setTimer2(void){
	//prescale set to 1
	//NOTE THAT T2CKPS0 = 0 NOT T2CKPS0 = X OR 1 T2CKPS0 MUST BE ZERO FOR 16 PRESCALE
	//postscale set to 9
	//clear timer value
	TMR2 = 0;	
	T2CON = 0b01000100;
}

void send40msgap(void){

	PR2 = SEQGAP;

	//prescale set to 16
	//NOTE THAT T2CKPS0 = 0 NOT T2CKPS0 = X OR 1 T2CKPS0 MUST BE ZERO FOR 16 PRESCALE
	//postscale set to 10
	//clear timer value
	TMR2 = 0;	
	T2CON = 0b01001110;

	//set up interrupt
	TMR2IF = 0;
	TMR2IE = 1;
	
	output38khz();

	//turn timer off
	//set up interrupt
	TMR2IE = 0;
	TMR2IF = 0;
}


void sendstartseq(void){

	char khzOut = 0;

	for(char x = 0; x < STNUMS; x++)
	{
		switch(x)
		{
			case 1:
				khzOut = 1;
				break;
			case 3:
				khzOut = 1;
				break;
			case 5:
				khzOut = 1;
				break;
			case 7:
				khzOut = 1;
				break;
			default:
				khzOut = 0;
				break;
		}	
	
		PR2 = start_seq[x];
		setTimer2();

		//set up interrupt
		TMR2IF = 0;
		TMR2IE = 1;		
		
		timing = 0;

		//if we do not need 38khz then output is zero and we just wait
		if(khzOut == 0)
		{
			while(timing == 0)
			{
				IR_LED = 0;
			}
		}
		else
		{
			output38khz();
		} //end if khzout

		//turn off interrupt
		TMR2IF = 0;
		TMR2IE = 0;		
	} //end for
}


void stopBtn(void){
	char kOut = 0;

	//disable timer0 until we are finished
	T0IE = 0;

	send40msgap();

	//send 3 sequences
	for(char t = 0; t < 3; t++)
	{
	 	sendstartseq();

		//send sequence 1
		for(char x = 0; x < SEQNUMS; x++)
		{
			kOut = need38khz(x);
		
			PR2 = stop_btn_seq1[x];
			setTimer2();

			//set up interrupt
			TMR2IF = 0;
			TMR2IE = 1;		
			
			timing = 0;
			
			//if we do not need 38khz then output is zero and we just wait
			if(kOut == 0)
			{
				while(timing == 0)
				{
					IR_LED = 0;
				}
			}
			else
			{
				output38khz();
			} //end if khzout
	
			//turn off interrupt
			TMR2IF = 0;
			TMR2IE = 0;	
		} //end for char x

		//send the gap between sequences
		send40msgap();

		//**** send sequence part 2
		sendstartseq();

		//send sequence 1
		for(char x = 0; x < SEQNUMS; x++)
		{
			kOut = need38khz(x);
		
			PR2 = stop_btn_seq2[x];
			setTimer2();

			//set up interrupt
			TMR2IF = 0;
			TMR2IE = 1;		

			timing = 0;
			
			//if we do not need 38khz then output is zero and we just wait
			if(kOut == 0)
			{
				while(timing == 0)
				{
					IR_LED = 0;
				}
			}
			else
			{
				output38khz();
			} //end if khzout
	
			//turn off interrupt
			TMR2IF = 0;
			TMR2IE = 0;	
		} //end for char x

		//send the gap between sequences
		send40msgap();
	} //end for char t			

	//enable timer0 as we are finished
	T0IE = 1;

	IR_LED = 0;
}

void recBtn(void){
	char kOut = 0;
 
	//disable timer0 until we are finished
	T0IE = 0;

	send40msgap();

	//send 3 sequences
	for(char t = 0; t < 3; t++)
	{
	 	sendstartseq();

		//send sequence 1
		for(char x = 0; x < SEQNUMS; x++)
		{
			kOut = need38khz(x);
		
			PR2 = rec_btn_seq1[x];
			setTimer2();

			//set up interrupt
			TMR2IF = 0;
			TMR2IE = 1;		
			
			timing = 0;
			
			//if we do not need 38khz then output is zero and we just wait
			if(kOut == 0)
			{
				while(timing == 0)
				{
					IR_LED = 0;
				}
			}
			else
			{
				output38khz();
			} //end if khzout
	
			//turn off interrupt
			TMR2IF = 0;
			TMR2IE = 0;	
		} //end for char x

		//send the gap between sequences
		send40msgap();

		//**** send sequence part 2
		sendstartseq();

		//send sequence 1
		for(char x = 0; x < SEQNUMS; x++)
		{
			kOut = need38khz(x);
		
			PR2 = rec_btn_seq2[x];
			setTimer2();

			//set up interrupt
			TMR2IF = 0;
			TMR2IE = 1;		

			timing = 0;
			
			//if we do not need 38khz then output is zero and we just wait
			if(kOut == 0)
			{
				while(timing == 0)
				{
					IR_LED = 0;
				}
			}
			else
			{
				output38khz();
			} //end if khzout
	
			//turn off interrupt
			TMR2IF = 0;
			TMR2IE = 0;	
		} //end for char x

		//send the gap between sequences
		send40msgap();
	} //end for char t			

	//enable timer0 as we are finished
	T0IE = 1;

	IR_LED = 0;
}


void StatusState(unsigned char setstate)
{
	//remember to keep each case of the switch time duration to match the STATUS_TIME define
	unsigned char t = 0;

	//1 - boot up - solid on
	//2 - motion detected - blink rapidly
	//3 - recording camera 1 - blink slowly
	//4 - recording camera 2 - blink twice quickly and then pause
	//5 - waiting or timing between record triggers - solid on

	//note:  keep all durations for each case the same to simplify handling the effects on delay loops.	
	switch(setstate){
		case 1:
			STATUS_LED = 0;
			break;
		case 2:
			for (t = 0; t < 3; t++)
			{
				STATUS_LED = 0;
				delayMs(50);
				STATUS_LED = 1;
				delayMs(50);
			} //end for t
			break;
		case 3:
			STATUS_LED = 0;
			delayMs(150);
			STATUS_LED = 1;
			delayMs(150);
			break;
		case 4:
			STATUS_LED = 0;
			delayMs(50);
			STATUS_LED = 1;
			delayMs(50);
			STATUS_LED = 0;
			delayMs(50);
			STATUS_LED = 1;
			delayMs(150);
			break;
		case 5:
			STATUS_LED = 0;
			break;	
		default:
			break;
	}
}


void selectCamera(unsigned char camneeded){

	if (camneeded)
	{
		if(CAM_SLCT)
		{				
			CAM_SLCT_RLY = 1;
		}
		else
		{
			CAM_SLCT_RLY = 0;
		}
	}
	else
	{
		if(CAM_SLCT)
		{				
			CAM_SLCT_RLY = 0;
		}
		else
		{
			CAM_SLCT_RLY = 1;
		}
	}
}

int main (void)
{
	unsigned char t = 0;
	unsigned char j = 0;
	unsigned long camera_dly_stpt = 0;
	unsigned long loopcnt = 0;
	unsigned long timeneeded = 0;

    config_io(); //Setup IO pins and defaults
	init_a2d();  //initialize analogs
	init_interrupts();  
    StatusState(1); //bootup
	IR_LED = 0;
	CAM_SLCT_RLY = 0; //make sure relay does not toggle
	stopBtn(); //IR LED stop recording
	selectCamera(0);
	
	//must delay at least 5 milliseconds after initializing analogs 
	//before reading them so we do a little extra time
	delayMs(50); 

	while(1)
	{  

		//must delay at least 5 milliseconds between reads which will happen as a result of the rest of the code
		camera_dly_stpt = read_a2d(CAMERA_DLY_STPT) * DELAY_MULTIPLIER; 
    	delayMs(50); //leave the delay for the a-d conversion 
		timeneeded = camera_dly_stpt * 1000/PAUSE_INTERVAL;
			
		for (loopcnt = 0; loopcnt < timeneeded; loopcnt++)                  
		{
			selectCamera(0);

			//check the sensor and exit if needed before delaying
			if (MOTION_SENSOR == 0)
			{
				break; //exit the for loop
			}

			StatusState(5);  //5 waiting
			delayMs(PAUSE_INTERVAL);  //do pause in 10 millisecond intervals			
		} //end for loopcnt

		recBtn(); //IR LED record
		
		//for loop to record cameras 1 and 2.
		for (t = 0; t < 2; t++)
		{
			if(t == 0)
			{
				camera_dly_stpt = read_a2d(CAMERA1_ON_STPT) / 3; //result is 1-255 thus yielding up to 4.25 minutes  
			}
			else
			{
				camera_dly_stpt = read_a2d(CAMERA2_ON_STPT) / 3; //result is 1-255 thus yielding up to 4.25 minutes  
			}

			delayMs(50); //leave the delay for the a-d conversion 
			timeneeded = camera_dly_stpt * 2 + 10;
		
			for (j = 0; j < timeneeded; j++)
			{
				if (MOTION_SENSOR == 0)
				{
					StatusState(2); //indicate motion sensor tripped
					//record the motion sensor protected camera right now	
					selectCamera(0);
				}
				else
				{
					//now it is time to record selected camera	
					selectCamera(t);

					if(t == 0)
					{
						StatusState(3); //indicate recording camera 1
					}
					else
					{
						StatusState(4); //indicate recording camera 2
					}
				} //end if else MOTION_SENSOR
			} //for j

			//10 second delay
			for (j = 0; j < 100; j++)
			{
				delayMs(100); //note that maximum is not 999 as would be expected
				if (MOTION_SENSOR == 0)
				{
					StatusState(2); //indicate motion sensor tripped
					//record the motion sensor protected camera right now	
					selectCamera(0);
				}
			}
		} //end for t

		//no longer recording if MOTION_SENSOR is not tripped but we need to provide 
		//video output for display for the user
		if(MOTION_SENSOR == 1)
		{	
			stopBtn(); //IR LED stop recording
		}

		//display camera selected by default		
		selectCamera(0);
	} //end while	
} //end main

